/**
 * Copyright (c) 2018-2023, NXOS Development Team
 * SPDX-License-Identifier: Apache-2.0
 * 
 * Contains: spin lock
 * 
 * Change Logs:
 * Date           Author            Notes
 * 2023-08-23     JasonHu           Init
 */

#include <nxos/spin.h>
#include <nxos/irq.h>

NX_Error NX_SpinInit(NX_Spin *lock)
{
    if (lock == NX_NULL)
    {
        return NX_EINVAL;
    }

    NX_AtomicSet(&lock->value, 0);
    return NX_EOK;
}

NX_Error NX_SpinTryLock(NX_Spin *lock)
{
    if (lock == NX_NULL)
    {
        return NX_EFAULT;
    }

    if (NX_AtomicCAS(&lock->value, 0, NX_SPIN_LOCK_VALUE) == 0)
    {
        return NX_EOK;
    }
    else
    {
        return NX_ERROR;
    }
}

NX_Error NX_SpinLock(NX_Spin *lock)
{
    if (lock == NX_NULL)
    {
        return NX_EFAULT;
    }
    do
    {
        NX_Error err;
        if ((err = NX_SpinTryLock(lock)) == NX_EOK)
        {
            break;
        }
    } while (1);

    return NX_EOK;
}

NX_Error NX_SpinUnlock(NX_Spin *lock)
{
    if (lock == NX_NULL)
    {
        return NX_EFAULT;
    }
    NX_AtomicSet(&lock->value, 0);
    return NX_EOK;
}

NX_Error NX_SpinLockIRQ(NX_Spin *lock, NX_UArch *level)
{
    NX_Error err;
    if (lock == NX_NULL || level == NX_NULL)
    {
        return NX_EINVAL;
    }
    *level = NX_IRQ_SaveLevel();
    if ((err = NX_SpinLock(lock)) != NX_EOK)
    {
        NX_IRQ_RestoreLevel(*level);
        *level = 0;
    }
    return err;
}

NX_Error NX_SpinTryLockIRQ(NX_Spin *lock, NX_UArch *level)
{
    NX_Error err;
    if (lock == NX_NULL || level == NX_NULL)
    {
        return NX_EINVAL;
    }
    *level = NX_IRQ_SaveLevel();
    if ((err = NX_SpinTryLock(lock)) != NX_EOK)
    {
        NX_IRQ_RestoreLevel(*level);
        *level = 0;
    }
    return err;
}

NX_Error NX_SpinUnlockIRQ(NX_Spin *lock, NX_UArch level)
{
    if (lock == NX_NULL)
    {
        return NX_EINVAL;
    }
    if (NX_SpinUnlock(lock) != NX_EOK)
    {
        return NX_EFAULT;
    }
    NX_IRQ_RestoreLevel(level);
    return NX_EOK;
}

NX_Bool NX_SpinIsLocked(NX_Spin *lock)
{
    if (lock == NX_NULL)
    {
        return NX_False;
    }

    if (NX_AtomicGet(&lock->value) != 0)
    {
        return NX_True;
    }
    return NX_False;
}
